﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace uam_fps_game.Gfx
{
    /// <summary>
    /// The supported light types.
    /// </summary>
    public enum LightType
    {
        POINT,
        SPOT,
        DIRECTIONAL
    }

    public class Light : VisualObject
    {

        /// <summary>
        /// The color of the Ambient Light.
        /// </summary>
        static public Vector3 AmbientColor
        {
            get { return new Vector3(mAmbientColorPower.X, mAmbientColorPower.Y, mAmbientColorPower.Z); }
            set
            {
                mAmbientColorPower.X = value.X;
                mAmbientColorPower.Y = value.Y;
                mAmbientColorPower.Z = value.Z;
            }
        }

        /// <summary>
        /// The power of the Ambient Light.
        /// </summary>
        static public float AmbientPower
        {
            get { return mAmbientColorPower.W; }
            set { mAmbientColorPower.W = value; }
        }

        static private Vector4 mAmbientColorPower = new Vector4(1.0f, 1.0f, 1.0f, 0.08f);

        /// <summary>
        /// The color and power of the ambient light where X, Y, Z are the color values and W is the power value.
        /// </summary>
        static public Vector4 AmbientColorPower
        {
            get { return mAmbientColorPower; }
            set { mAmbientColorPower = value; }
        }

        private Vector3 mPosition;

        /// <summary>
        /// Gets the absolute light position.
        /// </summary>
        public Vector3 Position
        {
            get { return mPosition; }
            protected set { mPosition = value; }
        }

        private Vector3 mDirection;

        /// <summary>
        /// Gets the absolute light direction.
        /// </summary>
        public Vector3 Direction
        {
            get { return mDirection; }
            protected set { mDirection = value; }
        }

        /// <summary>
        /// The type of the light.
        /// </summary>
        public LightType LType { get; protected set; }

        /// <summary>
        /// The direction vector of the light (not used by Point Lights).
        /// </summary>
        public Vector3 BaseDirection { get { return mBaseDirection; } set { mBaseDirection = value; } }
        private Vector3 mBaseDirection;

        /// <summary>
        /// The diffuse color of the light.
        /// </summary>
        public Vector3 DiffuseColor { get; set; }

        public Vector4 LightParams;

        public float Strength { get { return LightParams.Z; } set { LightParams.Z = value; } }
        public float Range { get { return LightParams.X; } set { LightParams.X = value; } }
        /// <summary>
        /// Controls how the light fades out as it approaches it's area of effect.
        /// </summary>
        public float FallOff { get { return LightParams.Y; } set { LightParams.Y = value; } }
        /// <summary>
        /// Spot Light specific attribute. The angle which defines the cone of the Spot Light.
        /// </summary>
        public float SpotAngle { get { return LightParams.W; } set { LightParams.W = value; } }
        /// <summary>
        /// Controls the light's ability to cast shadows.
        /// </summary>
        public bool IsGeneratingShadows { get; set; }

        private Matrix projMatrix;

        public Matrix LightProjection
        {
            get { return projMatrix; }
            private set { projMatrix = value; }
        }

        private Matrix viewMatrix;

        public Matrix LightView
        {
            get { return viewMatrix; }
            private set { viewMatrix = value; }
        }

        private Texture2D shadowMap;

        public Texture2D ShadowMap
        {
            get { return shadowMap; }
            internal set { shadowMap = value; }
        }
        

        internal Light(LightType type)
        {
            LType = type;

            BaseDirection = new Vector3(0, 1, 0);

            DiffuseColor = new Vector3(1, 1, 1);

            switch (type)
            {
                case LightType.POINT:
                    LightParams = new Vector4(1000.0f, 1.5f, 1.0f, 0);
                    break;
                case LightType.SPOT:
                    LightParams = new Vector4(6000.0f, 1.0f, 1.0f, 0.9f);
                    break;
                case LightType.DIRECTIONAL:
                    LightParams = new Vector4(float.MaxValue, 0, 0.6f, 0);
                    break;
                default:
                    break;
            }

            _objectSpaceBoundingSphere = new BoundingSphere(Vector3.Zero, LightParams.X);

            IsGeneratingShadows = false;

        }

        internal void CalcMatrices()
        {
            switch (LType)
	        {
		        case LightType.POINT:
                 break;
                case LightType.SPOT:
                    LightView = Matrix.CreateLookAt(Vector3.Zero + (35000 * Direction), Vector3.Zero, Vector3.Up);
                    LightProjection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, Renderer.mGraphicsDevice.Viewport.AspectRatio, 35000, 65000); 
                 break;
                case LightType.DIRECTIONAL:
                    LightView = Matrix.CreateLookAt(Vector3.Zero + (35000 * Direction), Vector3.Zero, Vector3.Up);
                    LightProjection = Matrix.CreateOrthographic(Renderer.mShadowMapSize, Renderer.mShadowMapSize, 35000, 65000);  // pierwszy i drugi parametr ? Raczej nie o to chodzi...
                 break;
                default:
                 break;
	        }
        }

        internal void UpdateSpatialParams()
        {
            Position = _worldMatrix.Translation;
            Vector3.TransformNormal(ref mBaseDirection, ref _worldMatrix, out mDirection);
        }

        //public override void Update(float deltaTime)
        //{
        //    Matrix parentMatrix = ParentNode.WorldMatrix;
        //    Position = parentMatrix.Translation;
        //    Vector3.TransformNormal(ref mBaseDirection, ref parentMatrix, out mDirection);
        //}
    }
}
